Many to Many Relationship on Flutter
Introduction
In the previous guide, we learned how to use one-to-many relations and we will continue with our project. In this guide, we will focus on the most common relation: many-to-many
. There are three ways to create a many-to-many relation in Parse.
- The first is using the Parse
Relations
, which is the fastest in creation and query time. We will use this in this guide. - The second is using
Arrays
ofPointers
which can lead to slow query times depending on their size. - The third is using
JoinTable
where the idea from classical database. When there is a many-to-many relation, we combine everyobjectId
orPointer
from both sides together to build a new separate table in which the relationship is tracked.
In this guide you will implement a many-to-many
relationship on a Flutter Book Registration App using the Parse Relations
. You will learn how to create and query many-to-many data relations and how to perform queries returning data from related objects, using Back4App and the Flutter SDK.
Prerequisites
To complete this tutorial, you will need:
- Android Studio or VS Code installed (with Plugins Dart and Flutter)
- The Flutter app created in previous guide.
- Note: Follow the One to many Relationship on Flutter
- Complete the previous guide so you can have a better understanding of the
one-to-may relationship
class.- A device (or virtual device) running Android or iOS.
Step 1 - Run the Book App Template
If you have not completed the previous guide you can clone and run the complete Book Flutter App project from our repository. You can also take a look at the previous guide to better understand the App template. Below you can find a visual representation of Book Registration data model.
Step 2 - Save a Book object and its Author’s
Open the Flutter project from the previous guide One to many Relationship on Flutter. Search for the function doSaveBook
in file main.dart
, and replace with the code below inside the Future<void> doSaveBook()
function below. This function will create a new Book in Back4app data store with relations.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
final book = ParseObject('Book')
..set('title', controllerTitle.text.trim())
..set('year', int.parse(controllerYear.text.trim()))
//the objectId will be converted to a Pointer on the save() method
..set('genre', ParseObject('Genre')..objectId = genre.objectId)
//you can also convert to a Pointer object before the saving using the .toPointer() method
..set('publisher',
(ParseObject('Publisher')..objectId = publisher.objectId).toPointer())
//Saving a List of Authors for the Book
..addRelation(
'authors',
authors
.map((o) => ParseObject('Author')..objectId = o.objectId)
.toList());
await book.save();
To build this function, follow these steps:
- 1. Create a new instance of the Parse
Book
class with the commandParseObject('Book')
. - 2. Use the
set
function to set the fields for this object.- 2.1.
title
is a text attributes that receive value from the text controller. - 2.2.
genre
receives the value by defining aParseObject
with theobjectId
of the selectedGenre
. (Parse will convert to pointer on save) - 2.3.
publisher
receives the value by defining aParseObject
with theobjectId
of the selectedPublisher
. (Note that we can specify for Parse that we want to save as apointer
using thetoPointer()
method) - 2.4.
authors
. We call theaddRelation
method of ParseObject, sending a list ofParseObject
with theobjectId
of the selectedAuthors
.
- 2.1.
- 3. Call the
save
function inParseObject
, which will effectively register the object to your database in the Back4app Dashboard.
Run the App and test the new doSaveBook()
function
- First, access the dashboard and delete the books that were previously registered in the previous guide.
- Click on the
Add Book
button. - Fill book information with Authors.
- Click on
Save Book
button
To confirm that the new object is save in the database with relations, you can access the Back4app Dashboard
and access Book
class.
Clicking on the object pointer/relation value in your dashboard will take you to the referenced object entry. It may seem like a harmless feature, but this makes debugging and error tracing much quicker than searching for it manually.
Step 3 - Query the Book Details with Relations
This function will query Book Details in Back4app database, returning relationship data. In some situations, you want to return multiple types of related objects in one query. You can do this with the includeObject
method. In our example, we want to return the books, with information from Genre and Publishers.
Search for the function getBookDetail
in the file main.dart
, then replace the code below inside getBookDetail(ParseObject book)
function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
QueryBuilder<ParseObject> queryBook =
QueryBuilder<ParseObject>(ParseObject('Book'))
..whereEqualTo('objectId', book.objectId)
..includeObject(['publisher', 'genre']);
final ParseResponse responseBook = await queryBook.query();
if (responseBook.success && responseBook.results != null) {
final book = (responseBook.results.first) as ParseObject;
bookTitle = book.get<String>('title');
bookYear = book.get<int>('year');
bookGenre = book.get<ParseObject>('genre').get<String>('name');
bookPublisher = book.get<ParseObject>('publisher').get<String>('name');
loadedData = true;
}
QueryBuilder<ParseObject> queryAuthors =
QueryBuilder<ParseObject>(ParseObject('Author'))
..whereRelatedTo('authors', 'Book', book.objectId);
final ParseResponse responseAuthors = await queryAuthors.query();
if (responseAuthors.success && responseAuthors.results != null) {
bookAuthors = responseAuthors.results
.map((e) => (e as ParseObject).get<String>('name'))
.toList();
}
To build this function, follow these steps:
- Create an instance of
ParseQuery
object forBook
class. Insert a condition in the query, to searchBooks
whereobjectId
field is equalobjectId
of the selected book. - We use the includeObject method, informing the fields of the pointers that we want to return the data in the same query: Genre and Publisher. You can also do multi level includeObject using dot notation. Exemple : `..includeObject([‘post’, ‘post.authors’]);
- Do a Query’s search method using
query()
method. - If the operations succeed, object in
Book
will be returned. - We use the get method to retrieve the data. For fields that are pointers, we will first need to retrieve the pointer, then obtain its data. Example:
bookGenre = book.get<ParseObject>('genre').get<String>('name');
In the second stage of processing, we need to recover the Authors associated with the Book.
To build this function, follow these steps:
- Create an instance of
ParseQuery
object forAuthors
class. Insert a condition in the query, usingwhereRelatedTo
operator to searchAuthors
relationship withBook
, whereBook
is equalobjectId
of the selected book. - Do a Query’s search method using
query()
method. - If the operations succeed, object in
Book
will be returned. - We use the get method to retrieve the data.
Run the App and test the new Query.
First, click on the List Publisher/Book
button.
Select a book from the list. The next screen will display the data for the books and their relationships.
It’s done!
At this point, you learned how to create and query many-to-many relations and how to perform queries returning data from related objects in Parse on Flutter
.