@weapnl/js-junction 0.0.11 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3 -0
- package/README.md +51 -0
- package/index.d.ts +5 -1
- package/package.json +1 -1
- package/src/builder/model.js +40 -2
- package/src/builder/properties/mediaCollections.js +28 -0
- package/src/request.js +14 -3
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ This package has support for Typescript (TS).
|
|
|
12
12
|
- [Creating Models](#creating-models)
|
|
13
13
|
- [Performing Requests](#performing-requests)
|
|
14
14
|
- [Applying Filters and Scopes](#applying-filters-and-scopes)
|
|
15
|
+
- [Uploading Files with Spatie Medialibrary](#uploading-files-with-spatie-medialibrary)
|
|
15
16
|
|
|
16
17
|
## Installation
|
|
17
18
|
```bash
|
|
@@ -371,3 +372,53 @@ api.removeHeader('HEADER NAME HERE'); // Removes the header.
|
|
|
371
372
|
**Sample response**
|
|
372
373
|
|
|
373
374
|
After executing a request, the property `response` contains a `Response` object, which has properties `statusCode`, `data` and `validation`.
|
|
375
|
+
|
|
376
|
+
### Uploading Files with [Spatie Medialibrary](https://spatie.be/docs/laravel-medialibrary/v11/introduction)
|
|
377
|
+
|
|
378
|
+
#### Step 1: Uploading Files to a Model
|
|
379
|
+
To upload files to a model, use the `upload` function available on the model instance. This function requires two arguments:
|
|
380
|
+
|
|
381
|
+
1. **Uploaded Files**: An array of files, typically obtained from an input field of `type="file"`.
|
|
382
|
+
2. **Collection Name**: The name of the media [collection](https://spatie.be/docs/laravel-medialibrary/v11/working-with-media-collections/simple-media-collections) to which the files should be attached. This corresponds to the collection defined in your Laravel model.
|
|
383
|
+
|
|
384
|
+
**Example Usage:**
|
|
385
|
+
```js
|
|
386
|
+
// Retrieve the employee model instance (e.g., Employee with ID 3)
|
|
387
|
+
const employee = Employee.show(3);
|
|
388
|
+
|
|
389
|
+
// Upload the files to the 'IdentityFiles' collection
|
|
390
|
+
employee.upload(uploadedFiles, 'IdentityFiles');
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
In this example, `uploadedFiles` is an array of files from an input field, and `'IdentityFiles'` is the name of the media collection on the `Employee` model where these files should be stored.
|
|
394
|
+
|
|
395
|
+
#### Step 2: Handling the Uploaded Files
|
|
396
|
+
Once the `upload` function is called, the files are sent to the API. The API temporarily stores these files in the media library and returns the media IDs associated with each file. These media IDs are automatically set on the model instance.
|
|
397
|
+
|
|
398
|
+
#### Step 3: Saving the Model with Attached Media
|
|
399
|
+
After uploading the files, you can call the `.save()` method on the model instance. This step finalizes the process by permanently attaching the uploaded files to the specified media collection on the model. The media IDs stored on the model are now linked to the correct collection in the database.
|
|
400
|
+
|
|
401
|
+
**Example of Saving the Model:**
|
|
402
|
+
```js
|
|
403
|
+
// Save the employee model with the uploaded files attached
|
|
404
|
+
employee.save();
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
When the `save()` method is invoked, the model is updated or created (depending on whether it was previously persisted), and the uploaded media files are attached to the correct collection as defined in the earlier steps.
|
|
408
|
+
|
|
409
|
+
#### Advanced Usage: Uploading Files in Nested Structures
|
|
410
|
+
If your model contains nested relationships, such as an `Employee` model with a `Contact` relationship, you can still use the `upload` function to attach files to the appropriate collection within the nested structure.
|
|
411
|
+
|
|
412
|
+
**Example with Nested Structure:**
|
|
413
|
+
```js
|
|
414
|
+
// Retrieve the employee model instance
|
|
415
|
+
const employee = Employee.show(3);
|
|
416
|
+
|
|
417
|
+
// Upload a profile picture to the 'ProfilePicture' collection within the 'Contact' relationship
|
|
418
|
+
employee.contact.upload(uploadedFiles, 'ProfilePicture');
|
|
419
|
+
|
|
420
|
+
// Save the employee model, including the nested contact with the attached profile picture
|
|
421
|
+
employee.save();
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
In this scenario, the uploaded files are linked to the `ProfilePicture` collection within the `Contact` relationship of the `Employee` model. When the `save()` method is called, the files are properly attached within the nested structure.
|
package/index.d.ts
CHANGED
|
@@ -81,7 +81,7 @@ declare class Request extends Mixins {
|
|
|
81
81
|
put(data?: object): Promise<this>;
|
|
82
82
|
setKey(key: string): this;
|
|
83
83
|
delete(): Promise<this>;
|
|
84
|
-
storeFiles(files?: object, data?: object): Promise<this>;
|
|
84
|
+
storeFiles(files?: object, data?: object, url?: string|null): Promise<this>;
|
|
85
85
|
readonly bodyParameters: object;
|
|
86
86
|
onSuccess<T = any>(callback?: (result: T, data: any) => void): this;
|
|
87
87
|
onError(callback?: (response: Response) => void): this;
|
|
@@ -112,6 +112,8 @@ export class Model extends Request {
|
|
|
112
112
|
|
|
113
113
|
static relations(): JsonMap;
|
|
114
114
|
|
|
115
|
+
static mediaCollections(): JsonMap;
|
|
116
|
+
|
|
115
117
|
static readonly endpoint: string;
|
|
116
118
|
|
|
117
119
|
readonly _identifier: number | string;
|
|
@@ -128,6 +130,8 @@ export class Model extends Request {
|
|
|
128
130
|
|
|
129
131
|
save(extraData?: JsonMap): Promise<Model>;
|
|
130
132
|
|
|
133
|
+
upload(files: object|object[], collection: string): Promise<this>;
|
|
134
|
+
|
|
131
135
|
clone(): Model;
|
|
132
136
|
|
|
133
137
|
_queryString(identifier?: number | string): string;
|
package/package.json
CHANGED
package/src/builder/model.js
CHANGED
|
@@ -2,6 +2,7 @@ import Accessors from './properties/accessors';
|
|
|
2
2
|
import Attributes from './properties/attributes';
|
|
3
3
|
import Counts from './properties/counts';
|
|
4
4
|
import Relations from './properties/relations';
|
|
5
|
+
import MediaCollections from './properties/mediaCollections';
|
|
5
6
|
import Request from '../request';
|
|
6
7
|
|
|
7
8
|
export default class Model extends Request {
|
|
@@ -12,6 +13,7 @@ export default class Model extends Request {
|
|
|
12
13
|
this._attributes = new Attributes(this);
|
|
13
14
|
this._counts = new Counts(this);
|
|
14
15
|
this._relations = new Relations(this);
|
|
16
|
+
this._mediaCollections = new MediaCollections(this);
|
|
15
17
|
|
|
16
18
|
this.setApi(api);
|
|
17
19
|
this.fill(defaults);
|
|
@@ -46,6 +48,7 @@ export default class Model extends Request {
|
|
|
46
48
|
...this._attributes.toJson(this),
|
|
47
49
|
...this._counts.toJson(this),
|
|
48
50
|
...this._relations.toJson(this),
|
|
51
|
+
...this._mediaCollections.toJson(this),
|
|
49
52
|
};
|
|
50
53
|
}
|
|
51
54
|
|
|
@@ -96,6 +99,15 @@ export default class Model extends Request {
|
|
|
96
99
|
return {};
|
|
97
100
|
}
|
|
98
101
|
|
|
102
|
+
/**
|
|
103
|
+
* The media collections of the model
|
|
104
|
+
*
|
|
105
|
+
* @returns {Object.<any, Object>}
|
|
106
|
+
*/
|
|
107
|
+
static mediaCollections () {
|
|
108
|
+
return {};
|
|
109
|
+
}
|
|
110
|
+
|
|
99
111
|
/**
|
|
100
112
|
* @returns {string} Endpoint of the model for the API.
|
|
101
113
|
*/
|
|
@@ -182,7 +194,7 @@ export default class Model extends Request {
|
|
|
182
194
|
|
|
183
195
|
this._response = await this._connection.post(
|
|
184
196
|
this._queryString(),
|
|
185
|
-
{ ...this._attributes.toJson(this), ...extraData },
|
|
197
|
+
{ ...this._attributes.toJson(this), ...this._mediaCollections.toJson(this), ...extraData },
|
|
186
198
|
);
|
|
187
199
|
|
|
188
200
|
this._connection.removeRequest(this);
|
|
@@ -210,7 +222,7 @@ export default class Model extends Request {
|
|
|
210
222
|
|
|
211
223
|
this._response = await this._connection.put(
|
|
212
224
|
this._queryString(this._identifier),
|
|
213
|
-
{ ...this._attributes.toJson(this), ...extraData },
|
|
225
|
+
{ ...this._attributes.toJson(this), ...this._mediaCollections.toJson(this), ...extraData },
|
|
214
226
|
);
|
|
215
227
|
|
|
216
228
|
this._connection.removeRequest(this);
|
|
@@ -245,6 +257,32 @@ export default class Model extends Request {
|
|
|
245
257
|
return !! this._response.data;
|
|
246
258
|
}
|
|
247
259
|
|
|
260
|
+
/**
|
|
261
|
+
* Upload an temporary media file to the API.
|
|
262
|
+
*
|
|
263
|
+
* @param {array|File} [files] The uploaded file or files.
|
|
264
|
+
* @param {string} [collection] The name of the file collection.
|
|
265
|
+
*
|
|
266
|
+
* @returns {array} The received media ids.
|
|
267
|
+
*/
|
|
268
|
+
async upload (files, collection) {
|
|
269
|
+
this._media ??= {};
|
|
270
|
+
const filesArray = (Array.isArray(files) ? files : [files]).filter((value) => value !== null);
|
|
271
|
+
|
|
272
|
+
if (filesArray.length === 0) {
|
|
273
|
+
this._media[collection] = {};
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const request = await this.storeFiles({
|
|
278
|
+
files: _.flatMapDeep(filesArray),
|
|
279
|
+
}, {}, '/media/upload');
|
|
280
|
+
|
|
281
|
+
this._media[collection] = request._response.data;
|
|
282
|
+
|
|
283
|
+
return request._response.data;
|
|
284
|
+
}
|
|
285
|
+
|
|
248
286
|
/**
|
|
249
287
|
* Save the current model. Based on the value of the identifier `store` or `update` will be called.
|
|
250
288
|
*
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @implements {Property}
|
|
3
|
+
*/
|
|
4
|
+
export default class MediaCollections {
|
|
5
|
+
/**
|
|
6
|
+
* @param {Model} model Instance of the model.
|
|
7
|
+
*/
|
|
8
|
+
constructor (model) {
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {Model} model
|
|
13
|
+
*
|
|
14
|
+
* @return {Object} The attributes casted to a json object.
|
|
15
|
+
*/
|
|
16
|
+
toJson (model) {
|
|
17
|
+
const json = {};
|
|
18
|
+
|
|
19
|
+
_.each(model.constructor.mediaCollections(), (options, key) => {
|
|
20
|
+
const mediaPrefix = '_media.';
|
|
21
|
+
let value = _.get(model, options.jsonKey ?? mediaPrefix + key, _.get(model, mediaPrefix + _.camelCase(key)));
|
|
22
|
+
|
|
23
|
+
_.set(json, mediaPrefix + key, value ?? []);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return json;
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/request.js
CHANGED
|
@@ -165,11 +165,12 @@ export default class Request {
|
|
|
165
165
|
/**
|
|
166
166
|
* @param {Object} files
|
|
167
167
|
* @param {Object} data
|
|
168
|
+
* @param {string|null} url
|
|
168
169
|
*
|
|
169
170
|
* @returns {this} The current instance.
|
|
170
171
|
*/
|
|
171
|
-
async storeFiles (files = {}, data = {}) {
|
|
172
|
-
|
|
172
|
+
async storeFiles (files = {}, data = {}, url = null) {
|
|
173
|
+
let queryUrl = url ?? this.url ?? this.constructor.endpoint;
|
|
173
174
|
|
|
174
175
|
this._connection.cancelRunning(this);
|
|
175
176
|
|
|
@@ -181,13 +182,23 @@ export default class Request {
|
|
|
181
182
|
|
|
182
183
|
const formData = this._createFormData(_.merge({}, files, data));
|
|
183
184
|
|
|
185
|
+
if (! _.isEmpty(this.bodyParameters)) {
|
|
186
|
+
queryUrl = `${queryUrl}?${this.bodyParameters}`;
|
|
187
|
+
}
|
|
188
|
+
|
|
184
189
|
this._response = await this._connection.post(
|
|
185
|
-
|
|
190
|
+
queryUrl,
|
|
186
191
|
formData,
|
|
187
192
|
);
|
|
188
193
|
|
|
189
194
|
this._connection.removeRequest(this);
|
|
190
195
|
|
|
196
|
+
this.setConfig({
|
|
197
|
+
headers: {
|
|
198
|
+
'Content-Type': 'application/json',
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
|
|
191
202
|
await this.triggerResponseEvents(this._response);
|
|
192
203
|
|
|
193
204
|
return this;
|