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 3 : final map = localAdapter.serialize(model).filterNulls; 7 : 8 1 : final relationships = <String, dynamic>{}; 9 : 10 4 : for (final field in localAdapter.relationshipsFor(model).keys) { 11 1 : final key = keyForField(field); 12 : 13 1 : if (map[field] != null) { 14 2 : if (map[field] is HasMany) { 15 1 : relationships[key] = 16 5 : (map[field] as HasMany).map((e) => e.id).toList(); 17 : } 18 2 : if (map[field] is BelongsTo) { 19 4 : relationships[key] = (map[field] as BelongsTo).value?.id; 20 : } 21 : } 22 1 : map.remove(field); 23 : } 24 : 25 2 : return map..addAll(relationships.filterNulls); 26 : } 27 : 28 1 : @override 29 : DeserializedData<T, DataModel> deserialize(Object? data, {String? key}) { 30 3 : final result = DeserializedData<T, DataModel>([], included: []); 31 : 32 1 : dynamic addIncluded(id, RemoteAdapter? adapter) { 33 1 : if (id is Map && adapter != null) { 34 1 : final data = adapter.deserialize(id as Map<String, dynamic>); 35 1 : result.included 36 2 : ..add(data.model as DataModel<DataModel>) 37 2 : ..addAll(data.included); 38 2 : return data.model!.id; 39 : } 40 : return id; 41 : } 42 : 43 1 : if (data == null || data == '') { 44 : return result; 45 : } 46 1 : if (data is Map) { 47 1 : data = [data]; 48 : } 49 : 50 1 : if (data is Iterable) { 51 2 : for (final mapIn in data) { 52 1 : final mapOut = <String, dynamic>{}; 53 : 54 2 : final relationships = localAdapter.relationshipsFor(); 55 : 56 2 : for (final mapInKey in mapIn.keys) { 57 2 : final mapOutKey = fieldForKey(mapInKey.toString()); 58 1 : final metadata = relationships[mapOutKey]; 59 : 60 : if (metadata != null) { 61 1 : final _type = metadata['type'] as String; 62 : 63 2 : if (metadata['kind'] == 'BelongsTo') { 64 3 : final id = addIncluded(mapIn[mapInKey], adapters[_type]); 65 1 : mapOut[mapOutKey] = id == null 66 : ? null 67 2 : : graph.getKeyForId(_type, id, 68 1 : keyIfAbsent: DataHelpers.generateKey(_type)); 69 : } 70 : 71 2 : if (metadata['kind'] == 'HasMany') { 72 2 : mapOut[mapOutKey] = (mapIn[mapInKey] as Iterable) 73 2 : .map((id) { 74 2 : id = addIncluded(id, adapters[_type]); 75 : return id == null 76 : ? null 77 2 : : graph.getKeyForId(_type, id, 78 1 : keyIfAbsent: DataHelpers.generateKey(_type)); 79 : }) 80 1 : .filterNulls 81 1 : .toImmutableList(); 82 : } 83 : } else { 84 : // regular field mapping 85 2 : mapOut[mapOutKey] = mapIn[mapInKey]; 86 : } 87 : } 88 : 89 2 : final model = localAdapter.deserialize(mapOut); 90 1 : if (model.id != null) { 91 2 : model._initialize(adapters, key: key, save: true); 92 2 : result.models.add(model); 93 : } 94 : } 95 : } 96 : 97 : return result; 98 : } 99 : 100 : /// A suffix appended to all [BelongsTo] relationships in serialized form. 101 : /// 102 : /// Example: 103 : /// 104 : /// ``` 105 : /// { 106 : /// "user_id": 1, 107 : /// "id": 1, 108 : /// "title": "delectus aut autem", 109 : /// "completed": false 110 : ///} 111 : ///``` 112 1 : @protected 113 : String get identifierSuffix => '_id'; 114 : 115 1 : Map<String, Map<String, Object?>> get _belongsTos => 116 2 : Map.fromEntries(localAdapter 117 1 : .relationshipsFor() 118 1 : .entries 119 5 : .where((e) => e.value['kind'] == 'BelongsTo')); 120 : 121 : /// Transforms a [key] into a model's field. 122 : /// 123 : /// This mapping can also be done via `json_serializable`'s `@JsonKey`. 124 : /// If both are used, `fieldForKey` will be applied before 125 : /// reaching `fromJson`. 126 1 : @protected 127 : @visibleForTesting 128 : String fieldForKey(String key) { 129 2 : if (key.endsWith(identifierSuffix)) { 130 : final keyWithoutId = 131 5 : key.substring(0, key.length - identifierSuffix.length); 132 3 : if (_belongsTos.keys.contains(keyWithoutId)) { 133 : return keyWithoutId; 134 : } 135 : } 136 : return key; 137 : } 138 : 139 : /// Transforms a model's [field] into a key. 140 : /// 141 : /// This mapping can also be done via `json_serializable`'s `@JsonKey`. 142 1 : @protected 143 : @visibleForTesting 144 : String keyForField(String field) { 145 3 : if (_belongsTos.keys.contains(field)) { 146 2 : return '$field$identifierSuffix'; 147 : } 148 : return field; 149 : } 150 : }