到目前为止,我们在资源方面做得很好,让我们再走一步,并定义一种使用boot
和extend
方法自定义资源输出的方法。
需求
为什么我们需要这些方法?好吧,有时我们需要自定义资源的输出,例如,我们可能需要向输出添加一个新密钥,或者我们可能需要从输出中删除键,或者我们可能需要更改一个值输出中的键。
这些更改主要是条件的,例如,如果当前资源为User Resource
,并且当前用户与资源中的用户相同,那么我们可能需要在输出中添加一个新密钥,例如假设我们要在输出中添加一个名为cart
的新密钥,但是我们只想在当前用户与资源中的用户相同时添加此密钥。
另一个用例,如果您正在使用课程API,并且您正在返回课程列表,或者只是一门课程,我们想在最终输出中添加一个新键而且,如果当前用户已订阅该课程,我们希望将此值设置为true
,否则将将密钥设置为false
或可能根本不返回,因此这是我们资源中要实现的must
功能。
引导并扩展
基本上,这是两种方法,在将我们的output
属性转换为最终输出之前,将调用第一个boot
,第二个extend
将在将我们的output
属性转换为最终输出后。
因此,您可以使用boot
在输出中添加新键,替换或从中删除任何键,您可以使用extend
将新键添加到输出,替换或从中删除任何键,但是在输出转换后。
执行
实现非常简单,这是两种方法(non-static of course
),在转换输出之前将调用第一个方法,而第二个方法将在转换输出后调用。
请注意,我们将进行这两种方法异步方法,以便您可以执行其中的任何异步操作,例如从数据库中获取数据。
// core/resources/resource.ts
// ...
export default class Resource {
/**
* Boot method
* Called before transforming the resource
*/
public async boot() {
//
}
/**
* Extend the resource output
* Called after transforming the resource
*/
public async extend() {
//
}
// ...
}
简单的两个async空方法,现在我们将更新我们的toJSON
函数为async
方法,并在转换输出之前和之后调用这两种方法。
// core/resources/resource.ts
// ...
export default class Resource {
// ...
/**
* Transform resource to object, that's going to be used as the final output
*/
public async toJSON() {
await this.boot();
await this.transformOutput();
await this.extend();
return this.data;
}
// ...
}
您可能会想知道,我们的toJSON
方法中的那个巨型代码块在哪里,我只是将其移至transformOutput
方法,现在更加干净。
我们还可以制作将在转换过程中使用的任何可呼叫功能也为async
。
异步转换
现在,让我们更新我们的transformOutput
方法也为async
方法。
在其中,将会打电话给transformValue
此方法必须为awaited
,以便我们也可以将其制作为async
。
// core/resources/resource.ts
// ...
export default class Resource {
// ...
/**
* Transform final output
*/
protected async transformOutput() {
for (const key in this.output) {
// first check if key is disabled
if (this.isDisabledKey(key)) continue;
if (!this.isAllowedKey(key)) continue;
// get value type
const valueType = this.output[key];
// now get the value from the given resource data
const value = get(
this.resource,
key,
get(this.defaults, key, missingKey),
);
if (value === missingKey) {
continue;
}
if (Is.empty(value)) continue;
if (Array.isArray(value)) {
this.data[key] = value.map(
async item => await this.transformValue(item, valueType),
);
} else {
this.data[key] = await this.transformValue(value, valueType);
}
}
}
// ...
}
我在transformOutput
方法中添加了async
关键字,然后我在transformValue
方法调用中添加了await
关键字。
请注意,我们必须将映射函数中的回调为
async
,因此我们可以await
transformValue
方法调用。
现在,让我们更新我们的transformValue
方法也为async
方法。
// core/resources/resource.ts
// ...
export default class Resource {
// ...
/**
* Transform final output
*/
protected async transformOutput() {
for (const key in this.output) {
// first check if key is disabled
if (this.isDisabledKey(key)) continue;
if (!this.isAllowedKey(key)) continue;
// get value type
const valueType = this.output[key];
// now get the value from the given resource data
const value = get(
this.resource,
key,
get(this.defaults, key, missingKey),
);
if (value === missingKey) {
continue;
}
if (Is.empty(value)) continue;
if (Array.isArray(value)) {
this.data[key] = value.map(
async item => await this.transformValue(item, valueType),
);
} else {
this.data[key] = await this.transformValue(value, valueType);
}
}
}
/**
* Transform value
*/
protected async transformValue(value: any, valueType: any) {
if (typeof valueType === "string") {
value = this.cast(value, valueType);
} else if (valueType.prototype instanceof Resource) {
if (!this.isValidResourceValue(value)) return null;
value = new valueType(value);
} else if (typeof valueType === "function") {
value = await valueType.call(this, value);
}
return value;
}
// ...
}
我在transformValue
方法中添加了async
关键字,然后我在valueType
函数调用中添加了await
关键字。
现在让我们在我们的UserResource
类中尝试一下。
// src/app/users/resources/user-resource.ts
import Resource from "core/resources/resource";
export default class UserResource extends Resource {
/**
* {@inheritDoc}
*/
public async boot(): Promise<void> {
this.data.welcome = true;
}
/**
* {@inheritDoc}
*/
public async extend(): Promise<void> {
this.data.goodbye = true;
}
}
现在您应该看到这样的东西:
{
"welcome": true,
"isActive": true,
"isPhoneVerified": false,
"name": "John Doe",
"email": "hassanzohdy@gmail.com",
"image": "http://127.0.0.1:3000/uploads/users/my-image.jpg",
"goodbye": true
},
但这确实无法正常工作,为什么?因为我们的方法toJSON
基本上是sync
方法,现在我们将其更改为async
方法,因此这将不再起作用。
这将是我们的下一个主题。
- 结论
在本章中,我们讨论了booting
和extending
资源的重要性,以及为什么它应该在我们的代码库中。
在下一章中,我们将看到如何使我们的toJSON
方法成为async
方法。
那是我们的电力响应班来的地方。
â•给我买一杯咖啡。
如果您喜欢我的文章并看到对您有用,则可以buy me a coffee,它将帮助我继续前进并继续创建更多内容。
ð项目存储库
您可以在Github
上找到此项目的最新更新ð加入我们的社区
加入我们的社区Discord获得帮助和支持(节点JS 2023频道)。
ð视频课程(阿拉伯语)
如果您想以视频格式学习此课程,则可以在Youtube上找到该课程,该课程是阿拉伯语。
ð奖励内容ð
您可能会看这些文章,这肯定会提高您的知识和生产力。
一般主题
- Event Driven Architecture: A Practical Guide in Javascript
- Best Practices For Case Styles: Camel, Pascal, Snake, and Kebab Case In Node And Javascript
- After 6 years of practicing MongoDB, Here are my thoughts on MongoDB vs MySQL
软件包和库
- Collections: Your ultimate Javascript Arrays Manager
- Supportive Is: an elegant utility to check types of values in JavaScript
- Localization: An agnostic i18n package to manage localization in your project
React JS软件包
课程(文章)