Flutter

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 of Pointers 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 every objectId or Pointer 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:

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.

flutter-back4app-associations

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 command ParseObject('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 a ParseObject with the objectId of the selected Genre. (Parse will convert to pointer on save)
    • 2.3. publisher receives the value by defining a ParseObject with the objectId of the selected Publisher. (Note that we can specify for Parse that we want to save as a pointer using the toPointer() method)
    • 2.4. authors. We call the addRelation method of ParseObject, sending a list of ParseObject with the objectId of the selected Authors.
  • 3. Call the save function in ParseObject, 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

flutter-back4app-associations

To confirm that the new object is save in the database with relations, you can access the Back4app Dashboard and access Book class.

flutter-back4app-associations

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:

  1. Create an instance of ParseQuery object for Book class. Insert a condition in the query, to search Books where objectId field is equal objectId of the selected book.
  2. 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’]);
  3. Do a Query’s search method using query() method.
  4. If the operations succeed, object in Book will be returned.
  5. 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:

  1. Create an instance of ParseQuery object for Authors class. Insert a condition in the query, using whereRelatedTo operator to search Authors relationship with Book, where Book is equal objectId of the selected book.
  2. Do a Query’s search method using query() method.
  3. If the operations succeed, object in Book will be returned.
  4. 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.

flutter-back4app-associations

Select a book from the list. The next screen will display the data for the books and their relationships.

flutter-back4app-associations

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.