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