Line data Source code
1 : part of flutter_data; 2 : 3 : mixin _RemoteAdapterSerialization<T extends DataModel<T>> on _RemoteAdapter<T> { 4 : @override 5 7 : Future<Map<String, dynamic>> serialize(T model, 6 : {bool withRelationships = true}) async { 7 : final map = 8 14 : localAdapter.serialize(model, withRelationships: withRelationships); 9 : 10 : // essentially converts keys to IDs 11 28 : for (final key in localAdapter.relationshipMetas.keys) { 12 14 : if (map[key] is Iterable) { 13 14 : map[key] = (map[key] as Iterable) 14 15 : .map((k) => graph.getIdForKey(k.toString())) 15 7 : .filterNulls 16 7 : .toList(); 17 7 : } else if (map[key] != null) { 18 10 : map[key] = graph.getIdForKey(map[key].toString()); 19 : } 20 14 : if (map[key] == null) map.remove(key); 21 : } 22 : return map; 23 : } 24 : 25 : @override 26 7 : Future<DeserializedData<T>> deserialize(Object? data) async { 27 21 : final result = DeserializedData<T>([], included: []); 28 : 29 4 : Future<Object?> _processIdAndAddInclude(id, RemoteAdapter? adapter) async { 30 4 : if (id is Map && adapter != null) { 31 6 : final data = await adapter.deserialize(id as Map<String, dynamic>); 32 3 : result.included 33 6 : ..add(data.model as DataModel<DataModel>) 34 6 : ..addAll(data.included); 35 6 : id = data.model!.id; 36 : } 37 : if (id != null && adapter != null) { 38 12 : return graph.getKeyForId(adapter.internalType, id, 39 8 : keyIfAbsent: DataHelpers.generateKey(adapter.internalType)); 40 : } 41 : return null; 42 : } 43 : 44 7 : if (data == null || data == '') { 45 : return result; 46 : } 47 : 48 : // since data is not null, touch local storage 49 14 : localAdapter._touchLocalStorage(); 50 : 51 7 : if (data is Map<String, dynamic>) { 52 7 : data = [data]; 53 : } 54 : 55 7 : if (data is Iterable) { 56 14 : for (final map in data) { 57 7 : final mapIn = Map<String, dynamic>.from(map as Map); 58 7 : final mapOut = <String, dynamic>{}; 59 : 60 14 : final relationships = localAdapter.relationshipMetas; 61 : 62 : // - process includes 63 : // - transform ids into keys to pass to the local deserializer 64 14 : for (final mapKey in mapIn.keys) { 65 7 : final metadata = relationships[mapKey]; 66 : 67 : if (metadata != null) { 68 5 : final relType = metadata.type; 69 : 70 10 : if (metadata.serialize == false) { 71 : continue; 72 : } 73 : 74 10 : if (metadata.kind == 'BelongsTo') { 75 4 : final key = await _processIdAndAddInclude( 76 6 : mapIn[mapKey], adapters[relType]!); 77 2 : if (key != null) mapOut[mapKey] = key; 78 : } 79 : 80 10 : if (metadata.kind == 'HasMany') { 81 8 : mapOut[mapKey] = [ 82 8 : for (final id in (mapIn[mapKey] as Iterable)) 83 12 : await _processIdAndAddInclude(id, adapters[relType]!) 84 4 : ].filterNulls; 85 : } 86 : } else { 87 : // regular field mapping 88 14 : mapOut[mapKey] = mapIn[mapKey]; 89 : } 90 : } 91 : 92 14 : final model = localAdapter.deserialize(mapOut); 93 14 : result.models.add(model); 94 : } 95 : } 96 : 97 : return result; 98 : } 99 : } 100 : 101 : /// A utility class used to return deserialized main [models] AND [included] models. 102 : class DeserializedData<T extends DataModel<T>> { 103 7 : const DeserializedData(this.models, {this.included = const []}); 104 : final List<T> models; 105 : final List<DataModel> included; 106 21 : T? get model => models.singleOrNull; 107 : 108 5 : void _log(RemoteAdapter adapter, DataRequestLabel label) { 109 20 : adapter.log(label, '${models.toShortLog()} fetched from remote'); 110 16 : final groupedIncluded = included.groupListsBy((m) => m._remoteAdapter.type); 111 7 : for (final e in groupedIncluded.entries) { 112 4 : if (e.value.isNotEmpty) { 113 10 : adapter.log(label, ' - with ${e.key} ${e.value.toShortLog()} '); 114 : } 115 : } 116 : } 117 : }