Line data Source code
1 : part of flutter_data;
2 :
3 : /// Thin wrapper on the [RemoteAdapter] API
4 : class Repository<T extends DataModel<T>> with _Lifecycle<Repository<T>> {
5 2 : String get type => DataHelpers.getType<T>();
6 :
7 : final _adapters = <String, RemoteAdapter>{};
8 :
9 4 : RemoteAdapter<T> get _adapter => _adapters[type] as RemoteAdapter<T>;
10 :
11 : /// ONLY FOR FLUTTER DATA INTERNAL USE
12 : ///
13 : /// It must remain non-private for the model extension to use.
14 1 : @protected
15 : @visibleForTesting
16 1 : RemoteAdapter<T> get internalAdapter => _adapter;
17 :
18 : /// Initializes this [Repository]. Nothing will work without this.
19 : /// In standard scenarios this initialization is done by the framework.
20 : @override
21 : @mustCallSuper
22 1 : FutureOr<Repository<T>> initialize(
23 : {bool remote,
24 : bool verbose,
25 : Map<String, RemoteAdapter> adapters,
26 : ProviderReference ref}) async {
27 1 : if (isInitialized) return this;
28 2 : _adapters.addAll(adapters);
29 3 : await _adapter.initialize(
30 : remote: remote, verbose: verbose, adapters: adapters, ref: ref);
31 2 : await super.initialize();
32 : return this;
33 : }
34 :
35 : /// Disposes this [Repository] and everything that depends on it.
36 : @override
37 : @mustCallSuper
38 1 : Future<void> dispose() async {
39 2 : await super.dispose();
40 3 : await _adapter?.dispose();
41 : }
42 :
43 : // public API
44 :
45 : /// Returns all models of type [T].
46 : ///
47 : /// If [_RemoteAdapter.shouldLoadRemoteAll] (function of [remote]) is `true`,
48 : /// it will initiate an HTTP call.
49 : /// Otherwise returns all models of type [T] in local storage.
50 : ///
51 : /// Arguments [params] and [headers] will be merged with
52 : /// [_RemoteAdapter.defaultParams] and [_RemoteAdapter.defaultHeaders], respectively.
53 : ///
54 : /// See also: [_RemoteAdapter.urlForFindAll], [_RemoteAdapter.methodForFindAll].
55 1 : Future<List<T>> findAll(
56 : {bool remote, Map<String, dynamic> params, Map<String, String> headers}) {
57 2 : return _adapter.findAll(
58 : remote: remote, params: params, headers: headers, init: true);
59 : }
60 :
61 : /// Returns model of type [T] by [id].
62 : ///
63 : /// If [_RemoteAdapter.shouldLoadRemoteOne] (function of [remote]) is `true`,
64 : /// it will initiate an HTTP call.
65 : /// Otherwise returns model of type [T] and [id] in local storage.
66 : ///
67 : /// Arguments [params] and [headers] will be merged with
68 : /// [_RemoteAdapter.defaultParams] and [_RemoteAdapter.defaultHeaders], respectively.
69 : ///
70 : /// See also: [_RemoteAdapter.urlForFindOne], [_RemoteAdapter.methodForFindOne].
71 1 : Future<T> findOne(final dynamic id,
72 : {bool remote, Map<String, dynamic> params, Map<String, String> headers}) {
73 2 : return _adapter.findOne(id,
74 : remote: remote, params: params, headers: headers, init: true);
75 : }
76 :
77 : /// Saves [model] of type [T].
78 : ///
79 : /// If [remote] is `true`, it will initiate an HTTP call.
80 : ///
81 : /// Always persists to local storage.
82 : ///
83 : /// Arguments [params] and [headers] will be merged with
84 : /// [_RemoteAdapter.defaultParams] and [_RemoteAdapter.defaultHeaders], respectively.
85 : ///
86 : /// See also: [_RemoteAdapter.urlForSave], [_RemoteAdapter.methodForSave].
87 1 : Future<T> save(T model,
88 : {bool remote, Map<String, dynamic> params, Map<String, String> headers}) {
89 2 : return _adapter.save(model,
90 : remote: remote, params: params, headers: headers, init: true);
91 : }
92 :
93 : /// Deletes [model] of type [T].
94 : ///
95 : /// If [remote] is `true`, it will initiate an HTTP call.
96 : ///
97 : /// Always deletes from local storage.
98 : ///
99 : /// Arguments [params] and [headers] will be merged with
100 : /// [_RemoteAdapter.defaultParams] and [_RemoteAdapter.defaultHeaders], respectively.
101 : ///
102 : /// See also: [_RemoteAdapter.urlForDelete], [_RemoteAdapter.methodForDelete].
103 1 : Future<void> delete(dynamic model,
104 : {bool remote, Map<String, dynamic> params, Map<String, String> headers}) {
105 2 : return _adapter.delete(model,
106 : remote: remote, params: params, headers: headers);
107 : }
108 :
109 : /// Deletes all models of type [T]. This ONLY affects local storage.
110 3 : Future<void> clear() => _adapter.clear();
111 :
112 : /// Watches changes on all models of type [T] in local storage.
113 : ///
114 : /// When called, will in turn call [findAll] with [remote], [params], [headers].
115 1 : DataStateNotifier<List<T>> watchAll(
116 : {bool remote, Map<String, dynamic> params, Map<String, String> headers}) {
117 2 : return _adapter.watchAll(remote: remote, params: params, headers: headers);
118 : }
119 :
120 : /// Watches changes on model of type [T] by [id] in local storage.
121 : ///
122 : /// Optionally [alsoWatch]es selected relationships of this model.
123 : ///
124 : /// Example: Watch `Book` with `id=1` and its `Author` relationship.
125 : ///
126 : /// ```
127 : /// bookRepository.watchOne('1', alsoWatch: (book) => [book.author]);
128 : /// ```
129 : ///
130 : /// When called, will in turn call [findAll] with [remote], [params], [headers].
131 1 : DataStateNotifier<T> watchOne(dynamic id,
132 : {bool remote,
133 : Map<String, dynamic> params,
134 : Map<String, String> headers,
135 : AlsoWatch<T> alsoWatch}) {
136 2 : return _adapter.watchOne(id,
137 : remote: remote, params: params, headers: headers, alsoWatch: alsoWatch);
138 : }
139 : }
140 :
141 : /// Annotation on a [DataModel] model to request a [Repository] be generated for it.
142 : ///
143 : /// Takes a list of [adapters] to be mixed into this [Repository].
144 : /// Public methods of these [adapters] mixins will be made available in the repository
145 : /// via extensions.
146 : ///
147 : /// A classic example is:
148 : ///
149 : /// ```
150 : /// @JsonSerializable()
151 : /// @DataRepository([JSONAPIAdapter])
152 : /// class Todo with DataModel<Todo> {
153 : /// @override
154 : /// final int id;
155 : /// final String title;
156 : /// final bool completed;
157 : ///
158 : /// Todo({this.id, this.title, this.completed = false});
159 : /// }
160 : ///```
161 : class DataRepository {
162 : final List<Type> adapters;
163 0 : const DataRepository(this.adapters);
164 : }
|