Line data Source code
1 : part of flutter_data;
2 :
3 : /// A `Set` that models a relationship between one or more [DataModel] objects
4 : /// and their a [DataModel] owner. Backed by a [GraphNotifier].
5 : abstract class Relationship<E extends DataModel<E>, N> with EquatableMixin {
6 11 : @protected
7 47 : Relationship(Set<E>? models) : this._(models?.map((m) => m._key!).toSet());
8 :
9 11 : Relationship._(this._uninitializedKeys);
10 :
11 3 : Relationship._remove() : _uninitializedKeys = {};
12 :
13 : String? _ownerKey;
14 : String? _name;
15 : String? _inverseName;
16 :
17 11 : RemoteAdapter<E> get _adapter =>
18 44 : internalRepositories[_internalType]!.remoteAdapter as RemoteAdapter<E>;
19 44 : GraphNotifier get _graph => _adapter.localAdapter.graph;
20 :
21 : final Set<String>? _uninitializedKeys;
22 22 : String get _internalType => DataHelpers.getType<E>();
23 :
24 22 : bool get isInitialized => _ownerKey != null;
25 :
26 : /// Initializes this relationship (typically when initializing the owner
27 : /// in [DataModel]) by supplying the owner, and related metadata.
28 11 : Relationship<E, N> initialize(
29 : {required final DataModel owner,
30 : required final String name,
31 : final String? inverseName}) {
32 11 : if (isInitialized) return this;
33 :
34 22 : _ownerKey = owner._key;
35 11 : _name = name;
36 11 : _inverseName = inverseName;
37 :
38 : // means it was omitted (remote-omitted, or loaded locally), so skip
39 11 : if (_uninitializedKeys == null) return this;
40 :
41 : // setting up from scratch, remove all and add keys
42 :
43 30 : _graph._removeEdges(_ownerKey!,
44 20 : metadata: _name!, inverseMetadata: _inverseName, notify: false);
45 :
46 : // in case node was removed during removeEdges
47 30 : _graph._addNode(_ownerKey!);
48 :
49 20 : _graph._addEdges(
50 10 : _ownerKey!,
51 10 : tos: _uninitializedKeys!,
52 10 : metadata: _name!,
53 10 : inverseMetadata: _inverseName,
54 : notify: false,
55 : );
56 20 : _uninitializedKeys!.clear();
57 :
58 : return this;
59 : }
60 :
61 : // implement collection-like methods
62 :
63 5 : bool _add(E value, {bool notify = true}) {
64 5 : if (_contains(value)) {
65 : return false;
66 : }
67 :
68 20 : _graph._addEdge(_ownerKey!, value._key!,
69 10 : metadata: _name!, inverseMetadata: _inverseName, notify: false);
70 : if (notify) {
71 8 : _graph._notify(
72 12 : [_ownerKey!, value._key!],
73 4 : metadata: _name,
74 : type: DataGraphEventType.addEdge,
75 : );
76 : }
77 :
78 : return true;
79 : }
80 :
81 5 : bool _contains(Object? element) {
82 10 : return _iterable.contains(element);
83 : }
84 :
85 4 : bool _remove(Object? value, {bool notify = true}) {
86 8 : assert(value is E);
87 : final model = value as E;
88 :
89 8 : _graph._removeEdge(
90 4 : _ownerKey!,
91 4 : model._key!,
92 4 : metadata: _name!,
93 4 : inverseMetadata: _inverseName,
94 : notify: false,
95 : );
96 : if (notify) {
97 6 : _graph._notify(
98 9 : [_ownerKey!, value._key!],
99 3 : metadata: _name,
100 : type: DataGraphEventType.removeEdge,
101 : );
102 : }
103 : return true;
104 : }
105 :
106 : // support methods
107 :
108 9 : Iterable<E> get _iterable {
109 63 : return _keys.map((key) => _adapter.localAdapter.findOne(key)).filterNulls;
110 : }
111 :
112 11 : Set<String> get _keys {
113 11 : if (!isInitialized) return {};
114 55 : return _graph._getEdge(_ownerKey!, metadata: _name!).toSet();
115 : }
116 :
117 4 : Set<Object> get _ids {
118 25 : return _keys.map((key) => _graph.getIdForKey(key)).filterNulls.toSet();
119 : }
120 :
121 2 : DelayedStateNotifier<DataGraphEvent> get _relationshipEventNotifier {
122 8 : return _adapter.graph.where((event) {
123 4 : return event.type.isEdge &&
124 6 : event.metadata == _name &&
125 6 : event.keys.containsFirst(_ownerKey!);
126 : });
127 : }
128 :
129 : DelayedStateNotifier<N> watch();
130 :
131 : /// This is used to make `json_serializable`'s `explicitToJson` transparent.
132 : ///
133 : /// For internal use. Does not return valid JSON.
134 8 : dynamic toJson() => this;
135 :
136 : /// Whether the relationship has a value.
137 6 : bool get isPresent => _iterable.isNotEmpty;
138 :
139 5 : @override
140 20 : List<Object?> get props => [_ownerKey, _name, _inverseName];
141 :
142 4 : @override
143 : String toString() {
144 : final keysWithoutId =
145 23 : _keys.where((k) => _graph.getIdForKey(k) == null).map((k) => '[$k]');
146 12 : return {..._ids, ...keysWithoutId}.join(', ');
147 : }
148 : }
149 :
150 : // annotation
151 :
152 : class DataRelationship {
153 : final String? inverse;
154 : final bool serialize;
155 34 : const DataRelationship({this.inverse, this.serialize = true});
156 : }
|