الرفرفة - Database Concepts Flutter - مفاهيم قواعد البيانات فلاطر

 الرفرفة - Database Concepts Flutter - مفاهيم قواعد البيانات فلاطر

الرفرفة - Database Concepts Flutter - مفاهيم قواعد البيانات فلاطر

الرفرفة - Database Concepts Flutter - مفاهيم قواعد البيانات فلاطر 

يوفر Flutter العديد من الحزم المتقدمة للعمل مع قواعد البيانات. أهم الباقات هي -

  • sqflite - يُستخدم للوصول إلى قاعدة بيانات SQLite ومعالجتها ، وملفات

  • firebase_database - يُستخدم للوصول إلى قاعدة بيانات NoSQL المستضافة على السحابة ومعالجتها من Google.

في هذا الفصل ، دعونا نناقش كل منهم بالتفصيل.

Sqlite و flutter 

قاعدة بيانات SQLite هي محرك قاعدة البيانات المضمنة الواقعي والقياسي المستند إلى SQL. إنه محرك قاعدة بيانات صغير وتم اختباره على مدار الوقت. توفر حزمة sqflite الكثير من الوظائف للعمل بكفاءة مع قاعدة بيانات SQLite. يوفر طرقًا قياسية لمعالجة محرك قاعدة بيانات SQLite. الوظائف الأساسية التي توفرها حزمة sqflite هي كما يلي -

  • إنشاء / فتح (طريقة OpenDatabase) قاعدة بيانات SQLite.

  • تنفيذ عبارة SQL (طريقة التنفيذ) ضد قاعدة بيانات SQLite.

  • طرق الاستعلام المتقدمة (طريقة الاستعلام) لتقليل التعليمات البرمجية المطلوبة للاستعلام والحصول على المعلومات من قاعدة بيانات SQLite.

لنقم بإنشاء تطبيق منتج لتخزين وجلب معلومات المنتج من محرك قاعدة بيانات SQLite قياسي باستخدام حزمة sqflite وفهم المفهوم الكامن وراء قاعدة بيانات SQLite وحزمة sqflite.

  • قم بإنشاء تطبيق Flutter جديد في Android studio ، product_sqlite_app.

  • استبدل رمز بدء التشغيل الافتراضي (main.dart) بكود product_rest_app الخاص بنا .

  • انسخ مجلد الأصول من product_nav_app إلى product_rest_app وأضف الأصول داخل ملف * pubspec.yaml`.

flutter: 
   assets: 
      - assets/appimages/floppy.png 
      - assets/appimages/iphone.png 
      - assets/appimages/laptop.png 
      - assets/appimages/pendrive.png 
      - assets/appimages/pixel.png 
      - assets/appimages/tablet.png
  • قم بتكوين حزمة sqflite في ملف pubspec.yaml كما هو موضح أدناه -

dependencies: sqflite: any

استخدم أحدث إصدار من sqflite بدلاً من أي

  • قم بتكوين حزمة path_provider في ملف pubspec.yaml كما هو موضح أدناه -

dependencies: path_provider: any
  • هنا ، يتم استخدام حزمة path_provider للحصول على مسار المجلد المؤقت للنظام ومسار التطبيق. استخدم أحدث إصدار من sqflite بدلاً من أي .

  • سينبه Android studio إلى تحديث pubspec.yaml.

محدث
  • انقر فوق خيار الحصول على التبعيات. سيحصل Android studio على الحزمة من الإنترنت ويقوم بتكوينها بشكل صحيح للتطبيق.

  • في قاعدة البيانات ، نحتاج إلى مفتاح أساسي ، معرف كحقل إضافي إلى جانب خصائص المنتج مثل الاسم والسعر وما إلى ذلك ، لذا ، أضف خاصية معرف في فئة المنتج. أضف أيضًا طريقة جديدة إلى خريطة لتحويل كائن المنتج إلى كائن الخريطة. يتم استخدام fromMap و toMap لإجراء تسلسل وإلغاء تسلسل كائن المنتج ويتم استخدامه في طرق معالجة قاعدة البيانات.

class Product { 
   final int id; 
   final String name; 
   final String description; 
   final int price; 
   final String image; 
   static final columns = ["id", "name", "description", "price", "image"]; 
   Product(this.id, this.name, this.description, this.price, this.image); 
   factory Product.fromMap(Map<String, dynamic> data) {
      return Product( 
         data['id'], 
         data['name'], 
         data['description'], 
         data['price'], 
         data['image'], 
      ); 
   } 
   Map<String, dynamic> toMap() => {
      "id": id, 
      "name": name, 
      "description": description, 
      "price": price, 
      "image": image 
   }; 
}
  • قم بإنشاء ملف جديد ، Database.dart في مجلد lib لكتابة وظائف SQLite ذات الصلة.

  • استيراد بيان الاستيراد الضروري في Database.dart.

import 'dart:async'; 
import 'dart:io'; 
import 'package:path/path.dart'; 
import 'package:path_provider/path_provider.dart'; 
import 'package:sqflite/sqflite.dart'; 
import 'Product.dart';
  • لاحظ النقاط التالية هنا -

    • المتزامن async   يستخدم لكتابة أساليب غير متزامن.

    • io يستخدم للوصول إلى الملفات والدلائل.

    • يستخدم path للوصول إلى وظيفة الأداة المساعدة dart core المتعلقة بمسارات الملفات.

    • يتم استخدام path_provider للحصول على مسار التطبيق المؤقت.

    • يستخدم sqflite لمعالجة قاعدة بيانات SQLite.

  • قم بإنشاء فئة جديدة SQLiteDbProvider

  • قم بتعريف كائن SQLiteDbProvider المستند إلى مفرد وثابت كما هو محدد أدناه -

class SQLiteDbProvider { 
   SQLiteDbProvider._(); 
   static final SQLiteDbProvider db = SQLiteDbProvider._(); 
   static Database _database; 
}
  • يمكن الوصول إلى كائن SQLiteDBProvoider وطريقته من خلال متغير db الثابت.

SQLiteDBProvoider.db.<emthod>
  • قم بإنشاء طريقة للحصول على قاعدة بيانات (خيار مستقبلي) من النوع Future <قاعدة بيانات>. إنشاء جدول المنتج وتحميل البيانات الأولية أثناء إنشاء قاعدة البيانات نفسها.

Future<Database> get database async { 
   if (_database != null) 
   return _database; 
   _database = await initDB(); 
   return _database; 
}
initDB() async { 
   Directory documentsDirectory = await getApplicationDocumentsDirectory(); 
   String path = join(documentsDirectory.path, "ProductDB.db"); 
   return await openDatabase(
      path, 
      version: 1,
      onOpen: (db) {}, 
      onCreate: (Database db, int version) async {
         await db.execute(
            "CREATE TABLE Product ("
            "id INTEGER PRIMARY KEY,"
            "name TEXT,"
            "description TEXT,"
            "price INTEGER," 
            "image TEXT" ")"
         ); 
         await db.execute(
            "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
            values (?, ?, ?, ?, ?)", 
            [1, "iPhone", "iPhone is the stylist phone ever", 1000, "iphone.png"]
         ); 
         await db.execute(
            "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
            values (?, ?, ?, ?, ?)", 
            [2, "Pixel", "Pixel is the most feature phone ever", 800, "pixel.png"]
         ); 
         await db.execute(
            "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
            values (?, ?, ?, ?, ?)", 
            [3, "Laptop", "Laptop is most productive development tool", 2000, "laptop.png"]\
         ); 
         await db.execute( 
            "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
            values (?, ?, ?, ?, ?)", 
            [4, "Tablet", "Laptop is most productive development tool", 1500, "tablet.png"]
         );
         await db.execute( 
            "INSERT INTO Product 
            ('id', 'name', 'description', 'price', 'image') 
            values (?, ?, ?, ?, ?)", 
            [5, "Pendrive", "Pendrive is useful storage medium", 100, "pendrive.png"]
         );
         await db.execute( 
            "INSERT INTO Product 
            ('id', 'name', 'description', 'price', 'image') 
            values (?, ?, ?, ?, ?)", 
            [6, "Floppy Drive", "Floppy drive is useful rescue storage medium", 20, "floppy.png"]
         ); 
      }
   ); 
}
  • هنا ، استخدمنا الدوال التالية -

    • getApplicationDocumentsDirectory - إرجاع مسار دليل التطبيق

    • Join - يُستخدم لإنشاء مسار خاص بالنظام. لقد استخدمناه لإنشاء مسار قاعدة البيانات.

    • openDatabase - يستخدم لفتح قاعدة بيانات SQLite

    • onOpen - يُستخدم لكتابة التعليمات البرمجية أثناء فتح قاعدة البيانات

    • onCreate - يُستخدم لكتابة التعليمات البرمجية أثناء إنشاء قاعدة بيانات لأول مرة

    • db.execute - يُستخدم لتنفيذ استعلامات SQL. يقبل الاستعلام. إذا كان الاستعلام يحتوي على عنصر نائب (؟) ، فإنه يقبل القيم كقائمة في الوسيطة الثانية.

  • اكتب دالة للحصول على جميع المنتجات في قاعدة البيانات -

Future<List<Product>> getAllProducts() async { 
   final db = await database; 
   List<Map> 
   results = await db.query("Product", columns: Product.columns, orderBy: "id ASC"); 
   
   List<Product> products = new List(); 
   results.forEach((result) { 
      Product product = Product.fromMap(result); 
      products.add(product); 
   }); 
   return products; 
}
  • هنا قمنا بما يلي للتعامل مع قواعد البيانات في الرفرفة flutter -

    • طريقة الاستعلام المستخدمة لجلب جميع معلومات المنتج. يوفر الاستعلام اختصارًا للاستعلام عن معلومات الجدول دون كتابة الاستعلام بالكامل. سيقوم التابع query بإنشاء الاستعلام المناسب نفسه باستخدام مدخلاتنا مثل الأعمدة ، orderBy ، وما إلى ذلك ،

    • تم استخدام طريقة fromMap للمنتج للحصول على تفاصيل المنتج من خلال تكرار كائن النتائج ، والذي يحتفظ بجميع الصفوف في الجدول.

  • اكتب دالة للحصول على منتج محدد للمعرف

Future<Product> getProductById(int id) async {
   final db = await database; 
   var result = await db.query("Product", where: "id = ", whereArgs: [id]); 
   return result.isNotEmpty ? Product.fromMap(result.first) : Null; 
}
  • هنا ، استخدمنا أين وأين Args لتطبيق عوامل التصفية.

  • إنشاء ثلاث دوال - إدراج وتحديث وحذف طريقة لإدراج المنتج وتحديثه وحذفه من قاعدة البيانات.

insert(Product product) async { 
   final db = await database; 
   var maxIdResult = await db.rawQuery(
      "SELECT MAX(id)+1 as last_inserted_id FROM Product");

   var id = maxIdResult.first["last_inserted_id"]; 
   var result = await db.rawInsert(
      "INSERT Into Product (id, name, description, price, image)" 
      " VALUES (?, ?, ?, ?, ?)", 
      [id, product.name, product.description, product.price, product.image] 
   ); 
   return result; 
}
update(Product product) async { 
   final db = await database; 
   var result = await db.update("Product", product.toMap(), 
   where: "id = ?", whereArgs: [product.id]); return result; 
} 
delete(int id) async { 
   final db = await database; 
   db.delete("Product", where: "id = ?", whereArgs: [id]); }
  • الكود النهائي لقاعدة البيانات dart هو كما يلي - 


import 'dart:async'; 
import 'dart:io'; 
import 'package:path/path.dart'; 
import 'package:path_provider/path_provider.dart'; 
import 'package:sqflite/sqflite.dart'; 
import 'Product.dart'; 

class SQLiteDbProvider {
   SQLiteDbProvider._(); 
   static final SQLiteDbProvider db = SQLiteDbProvider._(); 
   static Database _database; 
   
   Future<Database> get database async {
      if (_database != null) 
      return _database; 
      _database = await initDB(); 
      return _database; 
   } 
   initDB() async {
      Directory documentsDirectory = await 
      getApplicationDocumentsDirectory(); 
      String path = join(documentsDirectory.path, "ProductDB.db"); 
      return await openDatabase(
         path, version: 1, 
         onOpen: (db) {}, 
         onCreate: (Database db, int version) async {
            await db.execute(
               "CREATE TABLE Product (" 
               "id INTEGER PRIMARY KEY," 
               "name TEXT," 
               "description TEXT," 
               "price INTEGER," 
               "image TEXT"")"
            ); 
            await db.execute(
               "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
               values (?, ?, ?, ?, ?)", 
               [1, "iPhone", "iPhone is the stylist phone ever", 1000, "iphone.png"]
            ); 
            await db.execute( 
               "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
               values (?, ?, ?, ?, ?)", 
               [2, "Pixel", "Pixel is the most feature phone ever", 800, "pixel.png"]
            );
            await db.execute(
               "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
               values (?, ?, ?, ?, ?)", 
               [3, "Laptop", "Laptop is most productive development tool", 2000, "laptop.png"]
            ); 
            await db.execute( 
               "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
               values (?, ?, ?, ?, ?)", 
               [4, "Tablet", "Laptop is most productive development tool", 1500, "tablet.png"]
            ); 
            await db.execute( 
               "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
               values (?, ?, ?, ?, ?)", 
               [5, "Pendrive", "Pendrive is useful storage medium", 100, "pendrive.png"]
            );
            await db.execute( 
               "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
               values (?, ?, ?, ?, ?)", 
               [6, "Floppy Drive", "Floppy drive is useful rescue storage medium", 20, "floppy.png"]
            ); 
         }
      ); 
   }
   Future<List<Product>> getAllProducts() async {
      final db = await database; 
      List<Map> results = await db.query(
         "Product", columns: Product.columns, orderBy: "id ASC"
      ); 
      List<Product> products = new List();   
      results.forEach((result) {
         Product product = Product.fromMap(result); 
         products.add(product); 
      }); 
      return products; 
   } 
   Future<Product> getProductById(int id) async {
      final db = await database; 
      var result = await db.query("Product", where: "id = ", whereArgs: [id]); 
      return result.isNotEmpty ? Product.fromMap(result.first) : Null; 
   } 
   insert(Product product) async { 
      final db = await database; 
      var maxIdResult = await db.rawQuery("SELECT MAX(id)+1 as last_inserted_id FROM Product"); 
      var id = maxIdResult.first["last_inserted_id"]; 
      var result = await db.rawInsert(
         "INSERT Into Product (id, name, description, price, image)" 
         " VALUES (?, ?, ?, ?, ?)", 
         [id, product.name, product.description, product.price, product.image] 
      ); 
      return result; 
   } 
   update(Product product) async { 
      final db = await database; 
      var result = await db.update(
         "Product", product.toMap(), where: "id = ?", whereArgs: [product.id]
      ); 
      return result; 
   } 
   delete(int id) async { 
      final db = await database; 
      db.delete("Product", where: "id = ?", whereArgs: [id]);
   } 
}
  • قم بتغيير الدالة الرئيسية للحصول على معلومات المنتج.

void main() {
   runApp(MyApp(products: SQLiteDbProvider.db.getAllProducts())); 
}
  • هنا ، استخدمنا الدالة getAllProducts لجلب جميع المنتجات من قاعدة البيانات.

  • قم بتشغيل التطبيق وشاهد النتائج. سيكون مشابهًا للمثال السابق ، الوصول إلى واجهة برمجة تطبيقات خدمة المنتج ، باستثناء معلومات المنتج المخزنة وجلبها من قاعدة بيانات SQLite المحلية.

سحابة Firestore

Firebase عبارة عن نظام أساسي لتطوير تطبيقات BaaS. يوفر العديد من الميزات لتسريع تطوير تطبيقات الهاتف المحمول مثل خدمة المصادقة والتخزين السحابي وما إلى ذلك ، إحدى الميزات الرئيسية لـ Firebase هي Cloud Firestore ، وهي قاعدة بيانات NoSQL في الوقت الفعلي قائمة على السحابة.

يوفر Flutter حزمة خاصة ، cloud_firestore لبرمجتها مع Cloud Firestore. دعنا ننشئ متجرًا للمنتجات عبر الإنترنت في Cloud Firestore وننشئ تطبيقًا للوصول إلى متجر المنتج.

  • قم بإنشاء تطبيق Flutter جديد في Android studio ، product_firebase_app.

  • استبدل رمز بدء التشغيل الافتراضي (main.dart) بكود product_rest_app الخاص بنا .

  • انسخ ملف Product.dart من product_rest_app إلى مجلد lib.

class Product { 
   final String name; 
   final String description; 
   final int price; 
   final String image; 
   
   Product(this.name, this.description, this.price, this.image); 
   factory Product.fromMap(Map<String, dynamic> json) {
      return Product( 
         json['name'], 
         json['description'], 
         json['price'], 
         json['image'], 
      ); 
   }
}
  • انسخ مجلد الأصول من product_rest_app إلى product_firebase_app وأضف الأصول داخل ملف pubspec.yaml.

flutter:
   assets: 
   - assets/appimages/floppy.png 
   - assets/appimages/iphone.png 
   - assets/appimages/laptop.png 
   - assets/appimages/pendrive.png 
   - assets/appimages/pixel.png 
   - assets/appimages/tablet.png
  • قم بتكوين حزمة cloud_firestore في ملف pubspec.yaml كما هو موضح أدناه -

dependencies: cloud_firestore: ^0.9.13+1
  • هنا ، استخدم أحدث إصدار من حزمة cloud_firestore.

  • سوف يقوم Android studio بتنبيه أنه تم تحديث pubspec.yaml كما هو موضح هنا -

حزمة Cloud Firestore
  • انقر فوق خيار الحصول على التبعيات. سيحصل Android studio على الحزمة من الإنترنت ويقوم بتكوينها بشكل صحيح للتطبيق.

  • أنشئ مشروعًا في Firebase باتباع الخطوات التالية -

    • أنشئ حساب Firebase عن طريق تحديد خطة مجانية على https://firebase.google.com/pricing/.

    • بمجرد إنشاء حساب Firebase ، سيعيد التوجيه إلى صفحة نظرة عامة على المشروع. يسرد جميع المشاريع القائمة على Firebase ويوفر خيارًا لإنشاء مشروع جديد.

    • انقر فوق إضافة مشروع وسيتم فتح صفحة إنشاء المشروع.

    • أدخل تطبيق المنتجات db كاسم مشروع وانقر فوق خيار إنشاء مشروع.

    • انتقل إلى * وحدة تحكم Firebase.

    • انقر فوق نظرة عامة على المشروع. يفتح صفحة نظرة عامة على المشروع.

    • انقر فوق أيقونة android. سيفتح إعداد المشروع الخاص بتطوير Android.

    • أدخل اسم حزمة Android ، com.devlopertechnology. Blogspot.flutterapp.product_firebase_app.

    • انقر فوق تسجيل التطبيق. يقوم بإنشاء ملف تكوين المشروع ، google_service.json.

    • قم بتنزيل google_service.json ثم انقله إلى دليل التطبيق / android الخاص بالمشروع. هذا الملف هو الرابط بين تطبيقنا و Firebase.

    • افتح android / app / build.gradle وقم بتضمين الكود التالي -

apply plugin: 'com.google.gms.google-services'
    • افتح android / build.gradle وقم بتضمين التكوين التالي -

buildscript {
   repositories { 
      // ... 
   } 
   dependencies { 
      // ... 
      classpath 'com.google.gms:google-services:3.2.1' // new 
   } 
}

    هنا ، يتم استخدام المكون الإضافي ومسار الفصل لغرض قراءة ملف google_service.json.

    • افتح android / app / build.gradle وقم بتضمين الكود التالي أيضًا.

android {
   defaultConfig { 
      ... 
      multiDexEnabled true 
   } 
   ...
}
dependencies {
   ... 
   compile 'com.android.support: multidex:1.0.3' 
}

    تتيح هذه التبعية لتطبيق android استخدام وظائف dex المتعددة.

    • اتبع الخطوات المتبقية في Firebase Console أو تخطي فقط.

  • قم بإنشاء متجر منتج في المشروع الذي تم إنشاؤه حديثًا باستخدام الخطوات التالية -

    • انتقل إلى وحدة تحكم Firebase.

    • افتح المشروع الذي تم إنشاؤه حديثًا.

    • انقر فوق خيار قاعدة البيانات في القائمة اليسرى.

    • انقر فوق خيار إنشاء قاعدة بيانات.

    • انقر فوق ابدأ في وضع الاختبار ثم تمكين.

    • انقر فوق إضافة مجموعة. أدخل المنتج كاسم مجموعة ثم انقر فوق التالي.

    • أدخل نموذج معلومات المنتج كما هو موضح في الصورة هنا -

نموذج لمعلومات المنتج
  • أضف معلومات المنتج الإضافية باستخدام خيارات إضافة المستند .

  • افتح ملف main.dart واستورد ملف المكون الإضافي Cloud Firestore وإزالة حزمة http.

import 'package:cloud_firestore/cloud_firestore.dart';
  • قم بإزالة parseProducts وتحديث fetchProducts لجلب المنتجات من Cloud Firestore بدلاً من Product Service API.

Stream<QuerySnapshot> fetchProducts() { 
   return Firestore.instance.collection('product').snapshots(); }
  • هنا ، يتم استخدام طريقة Firestore.instance.collection للوصول إلى مجموعة المنتجات المتوفرة في المتجر السحابي. يوفر Firestore.instance.collection العديد من الخيارات لتصفية المجموعة للحصول على المستندات اللازمة. لكننا لم نطبق أي مرشح للحصول على جميع معلومات المنتج.

  • يوفر Cloud Firestore المجموعة من خلال مفهوم Dart Stream وبالتالي قم بتعديل نوع المنتجات في MyApp وعناصر واجهة MyHomePage من Future <list <Product>> إلى Stream <QuerySnapshot>.

  • قم بتغيير طريقة إنشاء عنصر واجهة مستخدم MyHomePage لاستخدام StreamBuilder بدلاً من FutureBuilder.

@override 
Widget build(BuildContext context) {
   return Scaffold(
      appBar: AppBar(title: Text("Product Navigation")), 
      body: Center(
         child: StreamBuilder<QuerySnapshot>(
            stream: products, builder: (context, snapshot) {
               if (snapshot.hasError) print(snapshot.error); 
               if(snapshot.hasData) {
                  List<DocumentSnapshot> 
                  documents = snapshot.data.documents; 
                  
                  List<Product> 
                  items = List<Product>(); 
                  
                  for(var i = 0; i < documents.length; i++) { 
                     DocumentSnapshot document = documents[i]; 
                     items.add(Product.fromMap(document.data)); 
                  } 
                  return ProductBoxList(items: items);
               } else { 
                  return Center(child: CircularProgressIndicator()); 
               }
            }, 
         ), 
      )
   ); 
}
  • هنا ، قمنا بإحضار معلومات المنتج كنوع قائمة <DocumentSnapshot>. نظرًا لأن عنصر واجهة المستخدم ProductBoxList الخاص بنا غير متوافق مع المستندات ، فقد قمنا بتحويل المستندات إلى نوع قائمة <المنتج> واستخدمناها كذلك.

  • أخيرًا ، قم بتشغيل التطبيق وشاهد النتيجة. منذ ذلك الحين ، استخدمنا نفس معلومات المنتج مثل تلك الخاصة بتطبيق SQLite وقمنا بتغيير وسيط التخزين فقط ، يبدو التطبيق الناتج مطابقًا لتطبيق SQLite .