برنامج JavaScript + Firestore التعليمي لعام 2020: تعلم بالمثال

Cloud Firestore هي قاعدة بيانات NoSQL فائقة السرعة وبدون خادم ، وهي مثالية لتشغيل تطبيقات الويب والأجهزة المحمولة من أي حجم. احصل على الدليل الكامل لتعلم Firestore ، الذي تم إنشاؤه لتوضيح كيفية استخدام Firestore كمحرك لمشاريعك المذهلة من الأمام إلى الخلف.

جدول المحتويات

الشروع في العمل مع Firestore

  • ما هو Firestore؟ لماذا يجب أن تستفيد منها؟
  • إعداد Firestore في مشروع JavaScript
  • وثائق ومجموعات Firestore
  • إدارة قاعدة البيانات الخاصة بنا باستخدام Firebase Console

جلب البيانات مع Firestore

  • الحصول على البيانات من مجموعة باستخدام .get ()
  • الاشتراك في مجموعة باستخدام .onSnapshot ()
  • الفرق بين .get () و .onSnapshot ()
  • إلغاء الاشتراك من مجموعة
  • الحصول على وثائق فردية

تغيير البيانات مع Firestore

  • إضافة مستند إلى مجموعة باستخدام .add ()
  • إضافة مستند إلى مجموعة باستخدام .set ()
  • تحديث البيانات الموجودة
  • حذف البيانات

الأنماط الأساسية

  • العمل مع المجموعات الفرعية
  • طرق مفيدة لحقول Firestore
  • الاستعلام باستخدام .where ()
  • طلب البيانات والحد منها

ملاحظة: يمكنك تنزيل إصدار PDF من هذا البرنامج التعليمي حتى تتمكن من قراءته في وضع عدم الاتصال.

ما هو Firestore؟ لماذا يجب أن تستفيد منها؟

يعد Firestore قاعدة بيانات مرنة للغاية وسهلة الاستخدام لتطوير الأجهزة المحمولة والويب والخوادم. إذا كنت معتادًا على قاعدة بيانات Firebase في الوقت الفعلي ، فإن Firestore بها العديد من أوجه التشابه ، ولكن مع واجهة برمجة تطبيقات مختلفة (يمكن القول إنها أكثر وضوحًا).

فيما يلي بعض الميزات التي يجلبها Firestore إلى الطاولة:

⚡️ سهولة الحصول على البيانات في الوقت الفعلي

مثل قاعدة بيانات Firebase في الوقت الفعلي ، يوفر Firestore طرقًا مفيدة مثل .onSnapshot () مما يجعل الاستماع إلى تحديثات بياناتك في الوقت الفعلي أمرًا سهلاً. يجعل من Firestore خيارًا مثاليًا للمشاريع التي تضع علاوة على عرض أحدث البيانات واستخدامها (تطبيقات الدردشة ، على سبيل المثال).

؟ المرونة كقاعدة بيانات NoSQL

يعد Firestore خيارًا مرنًا جدًا للواجهة الخلفية لأنه قاعدة بيانات NoSQL. تعني NoSQL أن البيانات لا يتم تخزينها في جداول وأعمدة مثل قاعدة بيانات SQL القياسية. إنه منظم مثل متجر ذي قيمة مفتاح ، كما لو كان كائن JavaScript كبير.

بمعنى آخر ، ليس هناك مخطط أو حاجة لوصف البيانات التي ستخزنها قاعدة البيانات الخاصة بنا. طالما أننا نقدم مفاتيح وقيم صالحة ، فسوف يقوم Firestore بتخزينها.

↕️ قابلة للتطوير بسهولة

إحدى الفوائد الرائعة لاختيار Firestore لقاعدة البيانات الخاصة بك هي البنية التحتية القوية للغاية التي يبني عليها والتي تمكنك من توسيع نطاق تطبيقك بسهولة بالغة. رأسيًا وأفقيًا. بغض النظر عما إذا كان لديك مئات أو ملايين المستخدمين. ستكون خوادم Google قادرة على التعامل مع أي حمل تضعه عليها.

باختصار ، يعد Firestore خيارًا رائعًا للتطبيقات الصغيرة والكبيرة. إنه قوي بالنسبة للتطبيقات الصغيرة لأنه يمكننا القيام بالكثير بدون الكثير من الإعداد وإنشاء المشاريع بسرعة كبيرة معهم. يعتبر Firestore مناسبًا تمامًا للمشاريع الكبيرة نظرًا لقدرته على التوسع.

إعداد Firestore في مشروع JavaScript

سنستخدم Firestore SDK لجافا سكريبت. خلال ورقة الغش هذه ، سنغطي كيفية استخدام Firestore في سياق مشروع JavaScript. على الرغم من ذلك ، فإن المفاهيم التي سنغطيها هنا يمكن نقلها بسهولة إلى أي من مكتبات عملاء Firestore المتاحة.

لبدء استخدام Firestore ، سنتوجه إلى وحدة تحكم Firebase. يمكنك زيارة ذلك بالذهاب إلى firebase.google.com. ستحتاج إلى أن يكون لديك حساب Google لتسجيل الدخول.

بمجرد تسجيل الدخول ، سننشئ مشروعًا جديدًا ونطلق عليه اسمًا.

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

سيعطينا هذا الكود الذي نحتاجه لدمج Firestore مع مشروع JavaScript الخاص بنا.

عادةً إذا كنت تقوم بإعداد هذا في أي نوع من تطبيقات JavaScript ، فستحتاج إلى وضع هذا في ملف مخصص يسمى firebase.js. إذا كنت تستخدم أي مكتبة JavaScript بها ملف package.json ، فستحتاج إلى تثبيت تبعية Firebase باستخدام npm أو الغزل.

// with npm npm i firebase // with yarn yarn add firebase

يمكن استخدام Firestore إما على العميل أو الخادم. إذا كنت تستخدم Firestore مع Node ، فستحتاج إلى استخدام بناء جملة CommonJS مع يتطلب. بخلاف ذلك ، إذا كنت تستخدم JavaScript في العميل ، فستقوم باستيراد Firebase باستخدام ES Modules.

// with Commonjs syntax (if using Node) const firebase = require("firebase/app"); require("firebase/firestore"); // with ES Modules (if using client-side JS, like React) import firebase from 'firebase/app'; import 'firebase/firestore'; var firebaseConfig = { apiKey: "AIzaSyDpLmM79mUqbMDBexFtOQOkSl0glxCW_ds", authDomain: "lfasdfkjkjlkjl.firebaseapp.com", databaseURL: "//lfasdlkjkjlkjl.firebaseio.com", projectId: "lfasdlkjkjlkjl", storageBucket: "lfasdlkjkjlkjl.appspot.com", messagingSenderId: "616270824980", appId: "1:616270824990:web:40c8b177c6b9729cb5110f", }; // Initialize Firebase firebase.initializeApp(firebaseConfig);

مجموعات ووثائق Firestore

هناك نوعان من المصطلحات الأساسية التي لا غنى عنها لفهم كيفية العمل مع Firestore: وثائق و مجموعات .

المستندات هي أجزاء فردية من البيانات في قاعدة بياناتنا. يمكنك التفكير في المستندات على أنها تشبه إلى حد كبير كائنات JavaScript البسيطة. وهي تتكون من أزواج ذات قيمة رئيسية ، والتي نشير إليها على أنها حقول . يمكن أن تكون قيم هذه الحقول سلاسل وأرقام و Booleans وكائنات ومصفوفات وحتى بيانات ثنائية.

document -> { key: value } 

تُعرف مجموعات هذه المستندات من هذه المستندات بالمجموعات. المجموعات تشبه إلى حد كبير مصفوفات العناصر. داخل المجموعة ، كل وثيقة مرتبطة بمعرف معين (المعرف).

collection -> [{ id: doc }, { id: doc }]

إدارة قاعدة البيانات الخاصة بنا باستخدام Firestore Console

قبل أن نبدأ بالفعل في العمل مع قاعدة البيانات الخاصة بنا ، نحتاج إلى إنشائها.

داخل وحدة تحكم Firebase الخاصة بنا ، انتقل إلى علامة التبويب "قاعدة البيانات" وأنشئ قاعدة بيانات Firestore الخاصة بك.

Once you've done that, we will start in test mode and enable all reads and writes to our database. In other words, we will have open access to get and change data in our database. If we were to add Firebase authentication, we could restrict access only to authenticated users.

After that, we'll be taken to our database itself, where we can start creating collections and documents. The root of our database will be a series of collections, so let's make our first collection.

We can select 'Start collection' and give it an id. Every collection is going to have an id or a name. For our project, we're going to keep track of our users' favorite books. We'll give our first collection the id 'books'.

Next, we'll add our first document with our newly-created 'books' collection.

Each document is going to have an id as well, linking it to the collection in which it exists.

In most cases we're going to use an  option to give it an automatically generated ID. So we can hit the button 'auto id' to do so, after which we need to provide a field, give it a type, as well as a value.

For our first book, we'll make a 'title' field of type 'string', with the value 'The Great Gatsby', and hit save.

After that, we should see our first item in our database.

Getting data from a collection with .get()

To get access Firestore use all of the methods it provides, we use firebase.firestore(). This method need to be executed every time we want to interact with our Firestore database.

أوصي بإنشاء متغير مخصص لتخزين مرجع واحد إلى Firestore. يساعد القيام بذلك على تقليل كمية الشفرة التي تكتبها عبر تطبيقك.

const db = firebase.firestore(); 
ومع ذلك ، في ورقة الغش هذه ، سألتزم باستخدام طريقة firestore في كل مرة لأكون واضحًا قدر الإمكان.

للإشارة إلى مجموعة ، نستخدم .collection()الطريقة ونقدم معرف المجموعة كوسيطة. للحصول على مرجع لمجموعة الكتب التي أنشأناها ، ما عليك سوى تمرير "الكتب" في السلسلة.

const booksRef = firebase.firestore().collection('books');

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

.get()يعيد وعدًا ، مما يعني أنه يمكننا حله إما باستخدام .then()رد الاتصال أو يمكننا استخدام صيغة انتظار انتظار غير متزامن إذا كنا ننفذ الكود الخاص بنا داخل وظيفة غير متزامنة.

Once our promises is resolved in one way or another, we get back what's known as a snapshot.

For a collection query that snapshot is going to consist of a number of individual documents. We can access them by saying snapshot.docs.

From each document, we can get the id as a separate property, and the rest of the data using the .data() method.

Here's what our entire query looks like:

const booksRef = firebase .firestore() .collection("books"); booksRef .get() .then((snapshot) => { const data = snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data(), })); console.log("All data in 'books' collection", data); // [ { id: 'glMeZvPpTN1Ah31sKcnj', title: 'The Great Gatsby' } ] });

Subscribing to a collection with .onSnapshot()

The .get() method simply returns all the data within our collection.

To leverage some of Firestore's realtime capabilities we can subscribe to a collection, which gives us the current value of the documents in that collection, whenever they are updated.

Instead of using the .get() method, which is for querying a single time, we use the .onSnapshot() method.

firebase .firestore() .collection("books") .onSnapshot((snapshot) => { const data = snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data(), })); console.log("All data in 'books' collection", data); });

In the code above, we're using what's known as method chaining instead of creating a separate variable to reference the collection.

What's powerful about using firestore is that we can chain a bunch of methods one after another, making for more declarative, readable code.

Within onSnapshot's callback, we get direct access to the snapshot of our collection, both now and whenever it's updated in the future. Try manually updating our one document and you'll see that .onSnapshot() is listening for any changes in this collection.

Difference between .get() and .onSnapshot()

The difference between the get and the snapshot methods is that get returns a promise, which needs to be resolved, and only then we get the snapshot data.

.onSnapshot, however, utilizes synchronous callback function, which gives us direct access to the snapshot.

This is important to keep in mind when it comes to these different methods--we have to know which of them return a promise and which are synchronous.

Unsubscribing from a collection with unsubscribe()

Note additionally that .onSnapshot() returns a function which we can use to unsubscribe and stop listening on a given collection.

This is important in cases where the user, for example, goes away from a given page where we're displaying a collection's data. Here's an example, using the library React were we are calling unsubscribe within the useEffect hook.

When we do so this is going to make sure that when our component is unmounted (no longer displayed within the context of our app) that we're no longer listening on the collection data that we're using in this component.

function App() { const [books, setBooks] = React.useState([]); React.useEffect(() => { const unsubscribe = firebase .firestore() .collection("books") .onSnapshot((snapshot) => { const data = snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data(), })); setBooks(data); }); }, []); return books.map(book => ) }

Getting Individual Documents with .doc()

When it comes to getting a document within a collection., the process is just the same as getting an entire collection: we need to first create a reference to that document, and then use the get method to grab it.

After that, however, we use the .doc() method chained on to the collection method. In order to create a reference, we need to grab this id from the database if it was auto generated. After that, we can chain on .get() and resolve the promise.

const bookRef = firebase .firestore() .collection("books") .doc("glMeZvPpTN1Ah31sKcnj"); bookRef.get().then((doc) => { if (!doc.exists) return; console.log("Document data:", doc.data()); // Document data: { title: 'The Great Gatsby' } });

Notice the conditional if (!doc.exists) return; in the code above.

Once we get the document back, it's essential to check to see whether it exists.

If we don't, there'll be an error in getting our document data. The way to check and see if our document exists is by saying, if doc.exists, which returns a true or false value.

If this expression returns false, we want to return from the function or maybe throw an error. If doc.exists is true, we can get the data from doc.data.

Adding document to a collection with .add()

Next, let's move on to changing data. The easiest way to add a new document to a collection is with the .add() method.

All you need to do is select a collection reference (with .collection()) and chain on .add().

Going back to our definition of documents as being like JavaScript objects, we need to pass an object to the .add() method and specify all the fields we want to be on the document.

Let's say we want to add another book, 'Of Mice and Men':

firebase .firestore() .collection("books") .add({ title: "Of Mice and Men", }) .then((ref) => { console.log("Added doc with ID: ", ref.id); // Added doc with ID: ZzhIgLqELaoE3eSsOazu });

The .add method returns a promise and from this resolved promise, we get back a reference to the created document, which gives us information such as the created id.

The .add() method auto generates an id for us. Note that we can't use this ref directly to get data. We can however pass the ref to the doc method to create another query.

Adding a document to a collection with .set()

Another way to add a document to a collection is with the .set() method.

Where set differs from add lies in the need to specify our own id upon adding the data.

This requires chaining on the .doc() method with the id that you want to use. Also, note how when the promise is resolved from .set(), we don't get a reference to the created document:

firebase .firestore() .collection("books") .doc("another book") .set({ title: "War and Peace", }) .then(() => { console.log("Document created"); });

Additionally, when we use .set() with an existing document, it will, by default, overwrite that document.

If we want to merge, an old document with a new document instead of overwriting it, we need to pass an additional argument to .set() and provide the property merge set to true.

// use .set() to merge data with existing document, not overwrite const bookRef = firebase .firestore() .collection("books") .doc("another book"); bookRef .set({ author: "Lev Nikolaevich Tolstoy" }, { merge: true }) .then(() => { console.log("Document merged"); bookRef .get() .then(doc => { console.log("Merged document: ", doc.data()); // Merged document: { title: 'War and Peace', author: 'Lev Nikolaevich Tolstoy' } }); });

Updating existing data with .update()

When it comes to updating data we use the update method, like .add() and .set() it returns a promise.

ما هو مفيد في الاستخدام .update()هو أنه ، على عكس .set()، لن يقوم بالكتابة فوق المستند بأكمله. أيضًا .set()، نحتاج إلى الإشارة إلى مستند فردي.

عند الاستخدام .update()، من المهم استخدام بعض معالجة الأخطاء ، مثل .catch()رد الاتصال في حالة عدم وجود المستند.

const bookRef = firebase.firestore().collection("books").doc("another book"); bookRef .update({ year: 1869, }) .then(() => { console.log("Document updated"); // Document updated }) .catch((error) => { console.error("Error updating doc", error); }); 

حذف البيانات باستخدام .delete ()

يمكننا حذف مجموعة مستندات معينة من خلال الرجوع إليها من خلال معرفها وتنفيذ .delete()الطريقة ، بهذه البساطة. كما أنه يعيد الوعد.

فيما يلي مثال أساسي لحذف كتاب بالمعرف "كتاب آخر":

firebase .firestore() .collection("books") .doc("another book") .delete() .then(() => console.log("Document deleted")) // Document deleted .catch((error) => console.error("Error deleting document", error));
لاحظ أن وثائق Firestore الرسمية لا توصي بحذف مجموعات كاملة ، فقط المستندات الفردية.

العمل مع المجموعات الفرعية

Let's say that we made a misstep in creating our application, and instead of just adding books we also want to connect them to the users that made them. T

The way that we want to restructure the data is by making a collection called 'users' in the root of our database, and have 'books' be a subcollection of 'users'. This will allow users to have their own collections of books. How do we set that up?

References to the subcollection 'books' should look something like this:

const userBooksRef = firebase .firestore() .collection('users') .doc('user-id') .collection('books');

Note additionally that we can write this all within a single .collection() call using forward slashes.

The above code is equivalent to the follow, where the collection reference must have an odd number of segments. If not, Firestore will throw an error.

const userBooksRef = firebase .firestore() .collection('users/user-id/books');

To create the subcollection itself, with one document (another Steinbeck novel, 'East of Eden') run the following.

firebase.firestore().collection("users/user-1/books").add({ title: "East of Eden", });

Then, getting that newly created subcollection would look like the following based off of the user's ID.

firebase .firestore() .collection("users/user-1/books") .get() .then((snapshot) => { const data = snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data(), })); console.log(data); // [ { id: 'UO07aqpw13xvlMAfAvTF', title: 'East of Eden' } ] });

Useful methods for Firestore fields

There are some useful tools that we can grab from Firestore that enables us to work with our field values a little bit easier.

For example, we can generate a timestamp for whenever a given document is created or updated with the following helper from the FieldValue property.

We can of course create our own date values using JavaScript, but using a server timestamp lets us know exactly when data is changed or created from Firestore itself.

firebase .firestore() .collection("users") .doc("user-2") .set({ created: firebase.firestore.FieldValue.serverTimestamp(), }) .then(() => { console.log("Added user"); // Added user });

Additionally, say we have a field on a document which keeps track of a certain number, say the number of books a user has created. Whenever a user creates a new book we want to increment that by one.

An easy way to do this, instead of having to first make a .get() request, is to use another field value helper called .increment():

const userRef = firebase.firestore().collection("users").doc("user-2"); userRef .set({ count: firebase.firestore.FieldValue.increment(1), }) .then(() => { console.log("Updated user"); userRef.get().then((doc) => { console.log("Updated user data: ", doc.data()); }); }); 

Querying with .where()

What if we want to get data from our collections based on certain conditions? For example, say we want to get all of the users that have submitted one or more books?

We can write such a query with the help of the .where() method. First we reference a collection and then chain on .where().

The where method takes three arguments--first, the field that we're searching on an operation, an operator, and then the value on which we want to filter our collection.

يمكننا استخدام أي من العوامل التالية ويمكن أن تكون الحقول التي نستخدمها قيمًا أولية بالإضافة إلى المصفوفات.

<، <=، ==، >، >=، array-contains، in، أوarray-contains-any

لجلب جميع المستخدمين الذين أرسلوا أكثر من كتاب واحد ، يمكننا استخدام الاستعلام التالي.

بعد .where()أن نحتاج إلى السلسلة .get(). عند حل وعدنا ، نحصل على ما يُعرف باسم لقطة استعلام .

تمامًا مثل الحصول على مجموعة ، يمكننا تكرار الاستعلام على لقطة الشاشة .map()للحصول على معرّف المستندات والبيانات (الحقول):

firebase .firestore() .collection("users") .where("count", ">=", 1) .get() .then((querySnapshot) => { const data = querySnapshot.docs.map((doc) => ({ id: doc.id, ...doc.data(), })); console.log("Users with > 1 book: ", data); // Users with > 1 book: [ { id: 'user-1', count: 1 } ] });
لاحظ أنه يمكنك ربط عدة .where()طرق لإنشاء استعلامات مركبة.

تحديد وترتيب الاستفسارات

Another method for effectively querying our collections is to limit them. Let's say we want to limit a given query to a certain amount of documents.

If we only want to return a few items from our query, we just need to add on the .limit() method, after a given reference.

If we wanted to do that through our query for fetching users that have submitted at least one book, it would look like the following.

const usersRef = firebase .firestore() .collection("users") .where("count", ">=", 1); usersRef.limit(3)

Another powerful feature is to order our queried data according to document fields using .orderBy().

If we want to order our created users by when they were first made, we can use the orderBy method with the 'created' field as the first argument. For the second argument, we specify whether it should be in ascending or descending order.

للحصول على ترتيب جميع المستخدمين عند إنشائهم من الأحدث إلى الأقدم ، يمكننا تنفيذ الاستعلام التالي:

const usersRef = firebase .firestore() .collection("users") .where("count", ">=", 1); usersRef.orderBy("created", "desc").limit(3);

يمكننا السلسلة .orderBy()مع .limit(). لكي يعمل هذا بشكل صحيح ، .limit()يجب أن يسمى أخيرًا وليس قبله .orderBy().

تريد نسختك الخاصة؟ ؟

إذا كنت ترغب في الحصول على هذا الدليل للرجوع إليه في المستقبل ، فقم بتنزيل ورقة غش لهذا البرنامج التعليمي بأكمله هنا.

انقر للاستيلاء على ورقة الغش