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 6 : (map[field] as HasMany).toSet().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 4 : 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 3 : 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 2 : model._initialize(adapters, key: key, save: true); 91 2 : result.models.add(model); 92 : } 93 : } 94 : 95 : return result; 96 : } 97 : 98 : /// A suffix appended to all [BelongsTo] relationships in serialized form. 99 : /// 100 : /// Example: 101 : /// 102 : /// ``` 103 : /// { 104 : /// "user_id": 1, 105 : /// "id": 1, 106 : /// "title": "delectus aut autem", 107 : /// "completed": false 108 : ///} 109 : ///``` 110 1 : @protected 111 : String get identifierSuffix => '_id'; 112 : 113 1 : Map<String, Map<String, Object?>> get _belongsTos => 114 2 : Map.fromEntries(localAdapter 115 1 : .relationshipsFor() 116 1 : .entries 117 5 : .where((e) => e.value['kind'] == 'BelongsTo')); 118 : 119 : /// Transforms a [key] into a model's field. 120 : /// 121 : /// This mapping can also be done via `json_serializable`'s `@JsonKey`. 122 : /// If both are used, `fieldForKey` will be applied before 123 : /// reaching `fromJson`. 124 1 : @protected 125 : @visibleForTesting 126 : String fieldForKey(String key) { 127 2 : if (key.endsWith(identifierSuffix)) { 128 3 : final keyWithoutId = key.substring(0, key.length - 3); 129 3 : if (_belongsTos.keys.contains(keyWithoutId)) { 130 : return keyWithoutId; 131 : } 132 : } 133 : return key; 134 : } 135 : 136 : /// Transforms a model's [field] into a key. 137 : /// 138 : /// This mapping can also be done via `json_serializable`'s `@JsonKey`. 139 1 : @protected 140 : @visibleForTesting 141 : String keyForField(String field) { 142 3 : if (_belongsTos.keys.contains(field)) { 143 2 : return '$field$identifierSuffix'; 144 : } 145 : return field; 146 : } 147 : }