3

I'm developing a mobile application that needs to store locally on user's device some information about the places he visited. These are my model classes:

class Place extends Equatable {
  final String placeId;
  final double latitude;
  final double longitude;
  final String formattedAddress;
  final String name;

  const Place({
    @required this.placeId,
    @required this.latitude,
    @required this.longitude,
    @required this.formattedAddress,
    @required this.name,
  });
}

class Trip extends Equatable {
  final int tripId;
  final String reason;
  final DateTime startingTime;
  final DateTime arrivalTime;
  final Place source;
  final Place destination;

  Trip({
    @required this.tripId,
    @required this.reason,
    @required this.startingTime,
    @required this.arrivalTime,
    @required this.source,
    @required this.destination,
  });
}

To store these information I'm using the package sqflite and to keep my code modular and well organized I created Data Access Objects (DAOs) for the two objects and I have a DatabaseHelper class to create the database and to abstract all the CRUD operations.

Despite this, I don't understand how can I store complex object like Trip in my database and how can I build a Trip object from a database row.

An object like Place only has fields that are primitive types, fully supported by sqflite. On the other hand Trip has two fields (source and destination) which are of type Place.

So what is the best way to serialize and deserialize an object like Trip?

This is my PlaceDao object:

class PlaceDao implements Dao<Place> {
  final tableName = 'places';
  final columnPlaceId = 'placeId';
  final columnLatitude = 'latitude';
  final columnLongitude = 'longitude';
  final columnFormattedAddress = 'formattedAddress';
  final columnName = 'name';

  @override
  String get createTableQuery {
    return '''
    CREATE TABLE $tableName (
      $columnPlaceId TEXT PRIMARY KEY,
      $columnLatitude REAL NOT NULL,
      $columnLongitude REAL NOT NULL,
      $columnFormattedAddress TEXT NOT NULL,
      $columnName TEXT NOT NULL
    )''';
  }

  @override
  List<Place> fromList(List<Map<String, dynamic>> mapsList) {
    return mapsList.map((map) => fromMap(map)).toList();
  }

  @override
  Place fromMap(Map<String, dynamic> map) {
    return Place(
      placeId: map[columnPlaceId],
      latitude: map[columnLatitude],
      longitude: map[columnLongitude],
      formattedAddress: map[columnFormattedAddress],
      name: map[columnName],
    );
  }

  @override
  Map<String, dynamic> toMap(Place place) {
    return {
      columnPlaceId: place.placeId,
      columnLatitude: place.latitude,
      columnLongitude: place.longitude,
      columnFormattedAddress: place.formattedAddress,
      columnName: place.name,
    };
  }
}

I don't undesrstand how can I return a Trip object in my fromMap() in the TripDao class, since in the table for my trips I only store the placeId for the source and destination fields, because they are foreign keys.

Suppose this is my TripDao class:

class TripDao implements Dao<Trip> {
  final tableName = 'trips';
  final columnTripId = 'tripId';
  final columnReason = 'reason';
  final columnStartingTime = 'startingTime';
  final columnArrivalTime = 'arrivalTime';
  final columnSourceId = 'sourceId';
  final columnDestinationId = 'destinationId';

  @override
  String get createTableQuery {
    return '''
    CREATE TABLE $tableName (
      $columnTripId INTEGER PRIMARY KEY AUTOINCREMENT,
      $columnReason TEXT NOT NULL,
      $columnStartingTime INTEGER NOT NULL,
      $columnArrivalTime INTEGER NOT NULL,
      $columnSourceId INTEGER NOT NULL,
      $columnDestinationId INTEGER NOT NULL
    )''';
  }

  @override
  List<Trip> fromList(List<Map<String, dynamic>> mapsList) {
    // ?????????????????????????????
  }

  @override
  Trip fromMap(Map<String, dynamic> map) {
    return Trip(
      tripId: map[columnTripId],
      reason: map[columnReason],
      startingTime: null,
      arrivalTime: null,
      source: null,           //<-- ?????????????????????????????
      destination: null,     //<-- ?????????????????????????????
    );
  }

  @override
  Map<String, dynamic> toMap(Trip trip) {
    return {
      columnTripId: trip.tripId,
      columnReason: trip.reason,
      columnStartingTime: null,
      columnArrivalTime: null,
      columnSourceId: trip.source.placeId,
      columnDestinationId: trip.destination.placeId,
    };
  }
}
Marco Gelli
  • 319
  • 2
  • 15
  • 1
    You should look into [moor](https://pub.dev/packages/moor). It's built on-top of sqflite and is intended to make these kinds of things much easier. – Abion47 May 18 '20 at 15:16
  • 1
    On a different note, if your DAOs aren't written in such a way to make this sort of conversion trivial, then they aren't DAOs but rather just some arbitrary intermediary data classes. In the database, the references to the `Place` objects would be replaced with foreign keys to a row in the `places` table, so the DAO should be created to reflect that. – Abion47 May 18 '20 at 15:19
  • You are totally right. I still don't understand which is the correct way to do this in my `TripDao` class. How would you fill the question marks in my `TripDao` class? @Abion47 – Marco Gelli May 18 '20 at 15:38
  • 1
    In the DAO, those fields would be integers that represent the foreign key relationship (i.e. the table ID) for those objects in the other table. When you convert the DAO to whatever object you are using for internal representation, you can take that integer and translate it into an actual `Place` object with a lookup table of some sort. – Abion47 May 18 '20 at 20:05

0 Answers0